from __future__ import absolute_import
from pyprotobuf.generators.javascript import Class, Property, Argument, Return, Call, DictionaryLiteral, ClassProperty, Literal, Enum, quote_literal, upper_name, ClosureBaseClass
import pyprotobuf.nodes as nodes
from pyprotobuf.codegenerator import CodeGenerator

# javascript has small int types, represented as strings
JS_TYPE_MAP = {
    'bool':'boolean',
    'bytes': 'string',
    'double': 'number',    
    'fixed32': 'number',
    'fixed64': 'string', 
    'float': 'number', 
    'int32': 'number',
    'int64': 'string',        
    'sfixed32': 'number',    
    'sfixed64': 'string',   
    'sint32': 'number',
    'sint64': 'string', 
    'string': 'string',
    'uint32': 'number',
    'uint64': 'string', 
}

class ClosureExterns(CodeGenerator):
    comment = '//'
    prefix = ''
    
    def generate_file(self, protonode, **kwargs):
        if kwargs.get('header'):
            self.output.write('/* Automatically generated. Do not modify this file.\n   %s\n*/\n' % protonode.filename)

        self.output_ast = []
        if protonode.has_option('javascript_package'):
            self.prefix = protonode.get_option('javascript_package')


        for node in protonode.children:
            self.call_visitor_method(node)

        for node in self.output_ast:
            self.output.write('\n\n')
            self.output.write(node.serialize())
            self.output.write(';\n')

        return self.output.to_string()
    
    def visit_OptionNode(self, node):
        """ Prefix the generated messages with javascript_package (if defined).

            XXX: just use the package statement?
        """
        if node.name == 'javascript_package':
            self.prefix = node.value.strip('"') + '.'

    def add_prefix(self, name):
        if self.prefix:
            return '{}{}'.format(self.prefix, name)
        return name

    def output_enum(self, node):
        enum_name = self.add_prefix(node.get_full_name())
        enum_assignments = node.get_children_of_type(nodes.EnumAssignmentNode)

        literal = DictionaryLiteral()

        for enum in enum_assignments:
            name, value = enum.name, enum.value
            literal.set_key(name, value)

        return Enum(enum_name, literal)

    def visit_MessageNode(self, node):
        fields = node.get_children_of_type(nodes.FieldDescriptorNode)
        fields = sorted(fields, key=lambda x: x.number)

        message_name = self.add_prefix(node.name)

        self.output_ast.append(Class(message_name))

        for enum in node.get_children_of_type(nodes.EnumNode):
            self.output_ast.append(self.output_enum(enum))

        for field in fields:
            if isinstance(field.type, nodes.MessageNode) or  isinstance(field.type, nodes.EnumNode):
                jstype = self.add_prefix(field.type.name)
            else:
                jstype = JS_TYPE_MAP.get(str(field.type), None)

            field_name = str(field.name)

            if field.label == "repeated":
                jstype = 'Array.<{}>'.format(jstype)

            self.output_ast.append(Property(field_name, parent=message_name, type=jstype))

            
__generator__ = ClosureExterns
